namespace Bread.Utility;

public static class VideoHelper
{
    public static void ParseFrameRate(double frameRate, out int FPS, out bool isNtsc)
    {
        double dot = frameRate - (int)frameRate;
        if (dot < 0.001) {
            FPS = (int)frameRate;
            isNtsc = false;
            return;
        }
        isNtsc = true;
        FPS = (int)(frameRate + 0.5);
    }


    public static void ParseFrameRate(double frameRate, out int num, out int den)
    {
        num = 0;
        den = 0;

        if (Math.Abs(frameRate - (int)frameRate) < 0.001) {
            num = (int)frameRate;
            den = 1;
            return;
        }

        if (Math.Abs(frameRate - 23.976) < 0.01) {
            //24000/1001
            num = 24000;
            den = 1001;
            return;
        }

        if (Math.Abs(frameRate - 29.97) < 0.01) {
            //30000/1001
            num = 30000;
            den = 1001;
            return;
        }

        if (Math.Abs(frameRate - 29.97) < 0.01) {
            //60000/1001
            num = 60000;
            den = 1001;
            return;
        }

        throw new NotSupportedException($"FrameRate:{frameRate} not supported");
    }


    /// 保持输入比例不变，在不裁剪的情况下，将输入矩形填充至目标矩形区域的最大居中位置
    /// </summary>
    /// <param name="dst">目标区域矩形</param>
    /// <param name="input">输入矩形尺寸</param>
    /// <returns>填充区域</returns>
    public static RectN LetterBox(this RectN dst, SizeN input)
    {
        RectN rect = RectN.Empty;

        if (dst.Width == 0 || dst.Height == 0 || input.Width == 0 || input.Height == 0) {
            return rect;
        }

        //宽高比, 数字越大说明越宽
        double inputRatio = input.Width / (double)input.Height;
        double destRatio = dst.Width / (double)dst.Height;

        if (destRatio > inputRatio) {
            // 输出区域更宽，使用列模式，输出区域两边冗余
            rect.Height = dst.Height;
            rect.Y = dst.Y;
            rect.Width = (int)(rect.Height * inputRatio);
            rect.X = (int)(dst.X + (dst.Width - rect.Width) / 2.0);
        }
        else {
            // 输入区域更宽，使用行模式，输出区域上下冗余
            rect.Width = dst.Width;
            rect.X = dst.X;
            rect.Height = (int)(rect.Width / inputRatio);
            rect.Y = (int)(dst.Y + (dst.Height - rect.Height) / 2.0);
        }
        return rect;
    }


    /// 保持输入比例不变，在不裁剪的情况下，将输入矩形填充至目标矩形区域的最大居中位置
    /// </summary>
    /// <param name="dst">目标区域矩形</param>
    /// <param name="input">输入矩形尺寸</param>
    /// <returns>填充区域</returns>
    public static RectD LetterBox(this RectD dst, SizeD input)
    {
        var rect = RectD.Empty;

        if (dst.Width == 0 || dst.Height == 0 || input.Width == 0 || input.Height == 0) {
            return rect;
        }

        //宽高比, 数字越大说明越宽
        var inputRatio = input.Width / input.Height;
        var destRatio = dst.Width / dst.Height; 
       
        if (destRatio > inputRatio) {
            // 输出区域更宽，使用列模式，输出区域两边冗余
            rect.Height = dst.Height;
            rect.Y = dst.Y;
            rect.Width = rect.Height * inputRatio;
            rect.X = dst.X + (dst.Width - rect.Width) / 2.0;
        }
        else {
            // 输入区域更宽，使用行模式，输出区域上下冗余
            rect.Width = dst.Width;
            rect.X = dst.X;
            rect.Height = rect.Width / inputRatio;
            rect.Y = dst.Y + (dst.Height - rect.Height) / 2.0;
        }
        return rect;
    }

    /// <summary>
    /// 获取采集时间 时:分:秒.帧数
    /// </summary>
    /// <returns>时:分:秒.帧数</returns>
    public static string GetVideoTimeInFrame(long hnsTime, float frameRate = 25)
    {
        long ONE_SECOND = 10000000;
        if (hnsTime <= 0) {
            return "00:00:00.00";
        }

        try {
            return (string.Format("{0:00}:{1:00}:{2:00}.{3:00}", hnsTime / ONE_SECOND / 3600, hnsTime / ONE_SECOND % 3600 / 60,
                hnsTime / ONE_SECOND % 60, (hnsTime % ONE_SECOND / 10000000.0) * frameRate));
        }
        catch {
            return "00:00:00.00";
        }
    }

    /// <summary>
    /// 获取采集时间 时:分:秒:毫秒
    /// </summary>
    /// <returns>时:分:秒:毫秒</returns>
    public static string GetVideoTimeInMillisecond(long hnsTime)
    {
        long ONE_SECOND = 10000000;
        if (hnsTime <= 0) {
            return "00:00:00:00";
        }

        try {
            return (string.Format("{0:00}:{1:00}:{2:00}:{3:00}", hnsTime / ONE_SECOND / 3600, hnsTime / ONE_SECOND % 3600 / 60,
                hnsTime / ONE_SECOND % 60, (hnsTime % ONE_SECOND / 10000000.0) * 1000));
        }
        catch {
            return "00:00:00:00";
        }
    }

    /// <summary>
    /// 获取采集时间 时:分:秒
    /// </summary>
    /// <param name="hnsTime"></param>
    /// <returns>时:分:秒</returns>
    public static string GetVideoTimeInSecondByHnsTime(long hnsTime)
    {
        long ONE_SECOND = 10000000;
        if (hnsTime <= 0) return "00:00:00";
        try {
            return (string.Format("{0:00}:{1:00}:{2:00}", hnsTime / ONE_SECOND / 3600,
                hnsTime / ONE_SECOND % 3600 / 60, hnsTime / ONE_SECOND % 60));
        }
        catch {
            return "00:00:00";
        }
    }

    /// <summary>
    /// 获取采集时间 时:分:秒
    /// </summary>
    /// <param name="hnsTime"></param>
    /// <param name="frameRate"></param>
    /// <returns>时:分:秒</returns>
    public static string GetVideoTimeInSecondByMillisecond(long milliseconds)
    {
        if (milliseconds <= 0) return "00:00:00";
        long seconds = milliseconds / 1000;
        return (string.Format("{0:00}:{1:00}:{2:00}", seconds / 3600, seconds % 3600 / 60, seconds % 60));
    }

    public static double GetMilliseconds(long hnsTime)
    {
        return hnsTime / 10000.0;
    }

    /// <summary>
    /// 获取采集时间 时:分:秒
    /// </summary>
    /// <param name="hnsTime"></param>
    /// <param name="frameRate"></param>
    /// <returns>时:分:秒</returns>
    public static string GetVideoTimeBySecond(double seconds, bool align = false)
    {
        if (seconds <= 0) return align ? "00:00:00" : "00:00";
        int s = (int)seconds;

        if (align) {
            return (string.Format("{0:00}:{1:00}:{2:00}", s / 3600, s % 3600 / 60, s % 60));
        }

        if(seconds < 3600) {
            return (string.Format("{0:00}:{1:00}", s / 60, s % 60));
        }
        else {
            return (string.Format("{0:00}:{1:00}:{2:00}", s / 3600, s % 3600 / 60, s % 60));
        }
    }


    /// <summary>
    /// duration in milliseconds
    /// </summary>
    /// <param name="path"></param>
    /// <returns></returns>
    public static long GetDurationByFFmpeg(string path)
    {
        try {
            if (!System.IO.File.Exists(path)) return 0;
            using (System.Diagnostics.Process pro = new System.Diagnostics.Process()) {
                pro.StartInfo.CreateNoWindow = true;
                pro.StartInfo.UseShellExecute = false;
                pro.StartInfo.ErrorDialog = false;
                pro.StartInfo.RedirectStandardError = true;

                pro.StartInfo.FileName = AppDomain.CurrentDomain.BaseDirectory + "ffmpeg.exe";
                pro.StartInfo.Arguments = " -i " + "\"" + path + "\"";

                pro.Start();
                System.IO.StreamReader errorreader = pro.StandardError;
                pro.WaitForExit(1000);

                string result = errorreader.ReadToEnd();
                if (!string.IsNullOrEmpty(result)) {
                    string duration = result.Substring(result.IndexOf("Duration: ") + ("Duration: ").Length, ("00:00:00").Length);
                    DateTime dt = DateTime.Now;
                    bool success = DateTime.TryParse(duration, out dt);
                    if (!success) return 0;

                    long time = (dt.Hour * 60 * 60 + dt.Minute * 60 + dt.Second) * 1000;
                    return time;
                }
                return 0;
            }
        }
        catch (Exception ex) {
            Log.Exception(ex);
            return 0;
        }
    }
}
